/*
 [The "BSD license"]
 Copyright (c) 2011-2011 Joel Li (李家智)
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
 3. The name of the author may not be used to endorse or promote products
     derived from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.bee.tl.core;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.bee.tl.core.io.ByteWriter;

public abstract class AbstractTemplate implements Template
{
	GroupTemplate group = null;
	Reader reader = null;
	BeeCommonNodeTree tree = null;
	Context context = new Context();
	String placeholderStart = "${";
	String placeholderEnd = "}";
	String statementStart = "<%";
	String statementEnd = "%>";

	Transformator tf = null;
	boolean isParsed = false;
	boolean isParseSuccess = false;
	// function,textprocess, format, virturalAttribute
	Map<String, Function> fnMap = new HashMap<String, Function>(0);
	Map<String, Class<Tag>> textProcessMap = new HashMap<String, Class<Tag>>(0);
	Map<String, Format> formatMap = new HashMap<String, Format>(0);
	List<VirtualAttributeEval> virtualAttributeList = new ArrayList<VirtualAttributeEval>(0);

	boolean isStrict = false;
	boolean nativeCall = false;
	boolean directByteOutput = false;
	Resource resource = null;

	public Resource getResource()
	{
		return resource;
	}

	public void setResource(Resource resource)
	{
		this.resource = resource;
	}

	public void enableNativeCall()
	{
		this.nativeCall = true;
	}

	public void enableStrict()
	{

		this.isStrict = isStrict;
	}

	public abstract void getText(Writer pw) throws IOException, BeeException;

	public void set(String name, Object o)
	{
		if (o instanceof Number)
		{
			context.set(name, new BeeNumber(o.toString()));
		}
		else
		{
			context.set(name, o);
		}

	}

	public void setRawValue(String name, Object o)
	{
		context.setRawValue(name, o);

	}

	public void setContext(Context ctx)
	{
		this.context = ctx;
	}

	public void getText(OutputStream os) throws IOException, BeeException
	{
		ByteWriter pw = null;
		if (directByteOutput)
		{
			pw = new ByteWriter(os, this.group.getCharset());
		}
		else
		{
			pw = new ByteWriter(new OutputStreamWriter(os, this.group.getCharset()));
		}
		this.getText(pw);

	}

	public String getTextAsString() throws IOException, BeeException
	{
		if (directByteOutput)
		{
			ByteArrayOutputStream os = new ByteArrayOutputStream();
			getText(os);
			byte[] result = os.toByteArray();
			return new String(result, group.charset);
		}
		else
		{
			StringWriter sw = new StringWriter();
			PrintWriter pw = new PrintWriter(sw);
			getText(pw);
			return sw.toString();
		}

	}

	public void setGroupTemplate(GroupTemplate group)
	{
		this.group = group;
		if (this.resource != null)
		{
			this.resource.setRoot(group.root);
		}

		// 预先设置，随后调用getText的时候以封装成ByteWriter
		this.directByteOutput = group.directByteOutput;
	}

	public GroupTemplate getGroupTemplate()
	{
		return this.group;
	}

	public void registerFunction(String name, Function fn)
	{
		if (this.containTag(name))
		{
			throw new RuntimeException("Function和Tag方法名不能重复");
		}
		this.fnMap.put(name, fn);
	}

	public void registerTagClass(String name, Class<Tag> process)
	{
		if (this.containFunction(name))
		{
			throw new RuntimeException("Tag和Function方法名不能重复");
		}
		this.textProcessMap.put(name, process);
	}

	public void registerFormat(String name, Format format)
	{
		this.formatMap.put(name, format);
	}

	public void registerVirtualAttributeEval(VirtualAttributeEval e)
	{
		virtualAttributeList.add(e);
	}

	public Function getFunction(String name)
	{
		//
		Function fn = this.group.getFunction(name);
		if (fn != null)
		{
			return fn;
		}
		else
		{
			return this.fnMap.get(name);
		}

	}

	public Tag getTag(String name)
	{

		Tag tag = this.group.getTag(name);
		if (tag == null)
		{
			Class classTag = this.textProcessMap.get(name);
			if (classTag == null)
			{
				return null;
			}
			try
			{
				return (Tag) classTag.newInstance();
			}
			catch (InstantiationException e)
			{
				return null;
			}
			catch (IllegalAccessException e)
			{
				return null;
			}
		}
		else
		{
			return tag;
		}

	}

	public Format getFormat(String name)
	{
		Format format = this.group.getFormat(name);
		if (format != null)
		{
			return format;
		}
		else
		{
			return this.formatMap.get(name);
		}

	}

	public VirtualAttributeEval getVirtualAttributeEval(Class c, String name)
	{

		VirtualAttributeEval geval = this.group.getVirtualAttributeEval(c, name);
		if (geval != null)
		{
			return geval;
		}
		else
		{
			for (VirtualAttributeEval eval : virtualAttributeList)
			{
				if (eval.isSuppoert(c, name))
				{
					return eval;
				}
			}
			return null;
		}

	}

	public String getPlaceholderStart()
	{
		return placeholderStart;
	}

	public void setPlaceholderStart(String placeholderStart)
	{
		this.placeholderStart = placeholderStart;
	}

	public String getPlaceholderEnd()
	{
		return placeholderEnd;
	}

	public void setPlaceholderEnd(String placeholderEnd)
	{
		this.placeholderEnd = placeholderEnd;
	}

	public String getStatementStart()
	{
		return statementStart;
	}

	public void setStatementStart(String statementStart)
	{
		this.statementStart = statementStart;
	}

	public String getStatementEnd()
	{
		return statementEnd;
	}

	public void setStatementEnd(String statementEnd)
	{
		this.statementEnd = statementEnd;
	}

	public Context getContext()
	{
		return this.context;
	}

	@Override
	public boolean containFunction(String name)
	{
		// TODO Auto-generated method stub
		if (!group.containFunction(name))
		{
			return this.fnMap.containsKey(name);
		}
		else
		{
			return true;
		}

	}

	@Override
	public boolean containTag(String name)
	{
		if (!group.containTag(name))
		{
			return this.textProcessMap.containsKey(name);
		}
		else
		{
			return true;
		}
	}

	@Override
	public boolean containFormat(String name)
	{
		if (!group.containFormat(name))
		{
			return this.formatMap.containsKey(name);
		}
		else
		{
			return true;
		}
	}

	public void enableDirectOutputByte()
	{
		this.directByteOutput = true;
	}

}
